package langnstats.project.lib.crossvalidation;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import langnstats.project.Global;
import langnstats.project.lib.LanguageModel;
import langnstats.project.lib.WordType;

public class CrossValidationTokenSet {
//	private WordType[][][] validationTokens;
	private TrainTokens[] trainTokens;
	private WordType[][] evalTokens;
	
//	private static final int TRAIN_SET_INDEX=0;
//	private static final int EVAL_SET_INDEX=1;
	
	
	public void exportTrainTokens(int i, File f) throws IOException{
		Global.exportTokens(this.getTrainTokens(i).getTokenArray(),f);
	}
	public void exportEvalTokens(int i, File f) throws IOException{
		Global.exportTokens(this.getEvalTokens(i),f);
	}
	
	public TrainTokens getTrainTokens(int i){
		return trainTokens[i];
	}
	public WordType[] getEvalTokens(int i){
		return evalTokens[i];
	}
	private void setEvalTokens(int i, WordType[] tokens){
		evalTokens[i] = tokens;
	}
	private void setTrainTokens(int i, TrainTokens cvtt){
		trainTokens[i] = cvtt;
	}
	public int getValidationSize(){ return this.evalTokens.length; }
	
	public static WordType[][] divide(WordType[] tokens, int n){
		Random r = new Random();
		int tokensLength = tokens.length;
		int start;
		while( (start=r.nextInt(tokensLength))>tokensLength*(n-1)/n );
		
		return divide(tokens,n,start);
	}
	public static WordType[][] divide(WordType[] tokens, int n, int start){
		int tokensLength = tokens.length;
		int cutPoint = tokensLength/n;
		WordType[][] returnValue = new WordType[2][];
		
		int i;
		for(i=start; !tokens[i].equals(WordType.PERIOD); i=(i+1)%tokensLength );
		i=(i+1)%tokensLength;	// start from a word next to PERIOD
		int realStart = i;
		
		for(int count=0;
		i<tokensLength && ( count<cutPoint || !tokens[i].equals(WordType.PERIOD));
		count++, i++);
		int realEnd = i+1;
		
		returnValue[1] = Arrays.copyOfRange(tokens, realStart, realEnd);
		returnValue[0] = (new TrainTokens(tokens, realStart, realEnd)).getTokenArray();
		return returnValue;
	}
	
	public CrossValidationTokenSet(WordType[] tokens, int n){
		this.trainTokens = new TrainTokens[n];
		this.evalTokens = new WordType[n][];
		
		int tokensLength = tokens.length;
		int prevCutPoint = 0;
		for(int i=0; i<n; i++){
			int cutPoint = (i+1)*tokensLength/n;
			
			int j;
			for(j=prevCutPoint; 
			j<cutPoint || (j<tokensLength && !tokens[j].equals(WordType.PERIOD));
			j++);
			
			cutPoint = j;
			if(cutPoint<tokensLength){ cutPoint++; } // include PERIOD which is the last character
			
			this.setEvalTokens(i, Arrays.copyOfRange(tokens, prevCutPoint, cutPoint));
			this.setTrainTokens(i, new TrainTokens(tokens, prevCutPoint, cutPoint));
			
			prevCutPoint = cutPoint;
		}
	}
	
	public CrossValidationResult crossValidate(LanguageModel model) {
		int n = this.getValidationSize();
		CrossValidationResult result = new CrossValidationResult(model.getDescription(), n);
			
		for(int i=0; i<n; i++){
			LanguageModel cvModel = model.clone();
			Global.debugOut.println("Starting cross-validation "+(i+1)+ " of model "+ cvModel.getDescription());
			TrainTokens trainTokens = this.getTrainTokens(i);
			WordType[] evalTokens = this.getEvalTokens(i);				
		
			Global.debugOut.println("Starting training model...");
			cvModel.train(trainTokens);
			
			Global.debugOut.println("Calculate Average-Log-Likelihood...");
			double ll = Global.getAverageLogLikelihood(cvModel, evalTokens);
			
			CrossValidationResultItem item = new CrossValidationResultItem(evalTokens.length, ll);
			Global.debugOut.println("Cross-validation result of "+(i+1)+": "+item.makeReport());
			result.add(item);	
		}
		return result;
	}
	public CrossValidationResult[] crossValidate(List<? extends LanguageModel> models) {
		CrossValidationResult[] results = new CrossValidationResult[models.size()];
		for(int i=0; i<models.size(); i++){
			
			LanguageModel model = models.get(i);
			Global.debugOut.println("Cross-validating model: "+model.getDescription());
			results[i] = this.crossValidate(model);
			Global.debugOut.println(results[i].makeReport());
		}
		return results;		
	}
}
